#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <asm/io.h>
#include <linux/sched.h>


#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>


#include "comm.h"

#define	   GPX2CONF		(0x11000c40)
#define	   GPX2DATA		(0x11000c44)


struct global_struct {

	/*中断相关的*/
	int irqno;
	int led_on;		/*0-off 1-on*/

	void *gpx2conf;
	void *gpx2data;

	char buf[32];

	/*字符设备相关的*/
	int major,minor;
	struct class *cls;
	struct device *device;
	struct cdev cdevobj;


	/*等待队列, 中断和字符设备交互 介质*/
	wait_queue_head_t wqhead;
	int data_flags;

	struct led_stat ledstat;

	/*异步通知的实现*/
	struct fasync_struct *fapp;
};

struct global_struct gstruct;



void led_init(void)
{

	int val;
	
	gstruct.gpx2conf = ioremap(GPX2CONF,4);
	gstruct.gpx2data = ioremap(GPX2DATA,4);

	/*配置 gpx2 7 管脚为输出模式*/
	val = readl(gstruct.gpx2conf);
	val = val & ( ~( 0xf<<28 ) );
	val = val |  (1<<28);
	writel(val,gstruct.gpx2conf);

	/*关灯*/
	val = readl(gstruct.gpx2data);
	val = val &  ( ~(1<<7) );
	writel(val,gstruct.gpx2data);

	return  ;
}

void led_ctrl(int on)
{
	int val;

	if(on == 0){
		/*关灯*/
		val = readl(gstruct.gpx2data);
		val = val &  ( ~(1<<7) );
		writel(val,gstruct.gpx2data);
	}else {
		/*关灯*/
		val = readl(gstruct.gpx2data);
		val = val | (1<<7);
		writel(val,gstruct.gpx2data);
	}
}



irqreturn_t  irq_handler_fun (int irqno, void *args)
{
	struct global_struct  *pt_gstruct = args;


#if		0
	pt_gstruct->led_on = 1 - pt_gstruct->led_on;


	led_ctrl(pt_gstruct->led_on);

	pt_gstruct->ledstat.on = pt_gstruct->led_on;

	snprintf(pt_gstruct->ledstat.des,sizeof(pt_gstruct->ledstat.des),
			"hello,current ledstat is %s",pt_gstruct->led_on==1?"on":"off");
#endif

	pt_gstruct->data_flags = 1;
	wake_up_interruptible(&pt_gstruct->wqhead);


	/*向用户层发送信号*/
	kill_fasync(&pt_gstruct->fapp,SIGIO,POLL_IN);

	printk("%s->%d  irqno%d %d\n",__func__,__LINE__,irqno,pt_gstruct->irqno);


	return IRQ_HANDLED; 		/*
		如果你的中断执行ok,那么 返回 IRQ_HANDLED
		如果你的中断 执行不ok,  返回  IRQ_NONE
	*/
}








int chdev_open (struct inode *inode, struct file *file)
{
	struct global_struct *pt_gstruct;

	pt_gstruct = container_of(inode->i_cdev,struct global_struct,cdevobj);
	file->private_data = pt_gstruct;
	
	return 0;
}

int chdev_release (struct inode *inode, struct file *file)
{

	return 0;
}

ssize_t chdev_read(struct file *file, char __user *usr, size_t size, loff_t *loff)
{
	int ret;
	struct global_struct *pt_gstruct = file->private_data;

	/* 如果 没有数据,该进程 进入睡眠,等待唤醒 
		如果与数据,不睡眠,做noting
	*/
	wait_event_interruptible(pt_gstruct->wqhead,pt_gstruct->data_flags);

	ret = copy_to_user(usr,&pt_gstruct->ledstat,size);
	pt_gstruct->data_flags = 0;

	
	return size;
}

int chdev_fasync(int fd, struct file *file, int on)
{
	struct global_struct *pt_gstruct = file->private_data;

	return fasync_helper(fd,file,on,&pt_gstruct->fapp);
}

long chdev_ioctl (struct file *file, unsigned int cmd, unsigned long args)
{
	int ret;
	struct global_struct *pt_gstruct = file->private_data;
	struct led_stat ledstat;

	pt_gstruct = pt_gstruct;
	
	switch(cmd){
	case LED_STAT_CTRL:
		ret = copy_from_user(&ledstat,(void *)args,sizeof(ledstat));
		led_ctrl(ledstat.on);
		break;
	default:
		printk("%s->%d unkown cmd %d\n",__func__,__LINE__,cmd);
		return -EBUSY;
	}

	return 0;
}


struct file_operations fops ={
	.open = chdev_open,

	.release = chdev_release,
	.read = chdev_read,
	.fasync = chdev_fasync,
	.unlocked_ioctl = chdev_ioctl,
};



int  md_init (void )
{
	struct device_node * of_node;
	int irqno;
	int ret;

	dev_t devno;

	/*############		 中断初始化  ############*/
	
	/*找到 按键的节点,返回 该节点指针*/
	of_node = of_find_node_by_path("/key2node");
	if(!of_node ){
		printk("%s->%d of_find_node_by_path err\n",__func__,__LINE__);
		return -13;
	}

	irqno = irq_of_parse_and_map(of_node,0);
	if(irqno <0){
		printk("%s->%d irq_of_parse_and_map err\n",__func__,__LINE__);
		return -14;
	}

	printk("%s->%d get key2node irqno %d\n",__func__,__LINE__,irqno);


	gstruct.irqno = irqno;

	ret = request_irq(irqno,irq_handler_fun,
				IRQF_TRIGGER_FALLING,"key2nodeintr",&gstruct);
	if(ret <0){
		printk("%s->%d request_irq err\n",__func__,__LINE__);
		return -15;
	}

	gstruct.led_on = 0;
	led_init();		



	/*###########	字符设备初始化	###########*/


	/*第一步 向内核 申请设备号*/
	ret = alloc_chrdev_region(&devno,0,1,"chdev test devno");
	if(ret <0 ){
		printk("%s->%d alloc_chrdev_region err%d\n",__func__,__LINE__,ret);
		return -12;
	}

	gstruct.major = MAJOR(devno );
	gstruct.minor = MINOR(devno );
	devno=  MKDEV(gstruct.major,gstruct.minor);
	printk("%s->%d get major%d  minor%d devno%d\n",
			__func__,__LINE__,gstruct.major,gstruct.minor,devno);


	/*第二步  创建一个字符设备对象*/
	cdev_init(&gstruct.cdevobj,&fops);
	ret = cdev_add(&gstruct.cdevobj,devno,1);
	if(ret <0){
		printk("%s->%d cdev_add err%d\n",__func__,__LINE__,ret);
		return -13;
	}


	/*字符设备第三步
		在用户层 创建字符设备节点
	*/

	gstruct.cls = class_create(THIS_MODULE,"chdev class");
	if(!gstruct.cls){
		printk("%s->%d class_create err \n",__func__,__LINE__ );
		return -14;
	}

	/*会在 /dev目录下 创建  /dev/chdev0 的设备节点/文件    */
	gstruct.device = device_create(gstruct.cls,NULL,devno,NULL,"chdev%d",0);
	if(!gstruct.device){
		printk("%s->%d device_create err \n",__func__,__LINE__ );
		return -15;
	}


	/*等待队列 初始化*/
	init_waitqueue_head(&gstruct.wqhead);
	gstruct.data_flags = 0 ;

	printk("%s->%d\n",__func__,__LINE__);
	return 0;	
}


void  md_exit (void )
{

	/*字符设备的释放*/
	device_destroy(gstruct.cls,MKDEV(gstruct.major,gstruct.minor));
	class_destroy(gstruct.cls);
	cdev_del(&gstruct.cdevobj);
	unregister_chrdev_region(MKDEV(gstruct.major,gstruct.minor),1);

	/*释放中断*/
	free_irq(gstruct.irqno,&gstruct);

	printk("%s->%d irqno%d\n",__func__,__LINE__,gstruct.irqno);
}



module_init(md_init);
module_exit(md_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("WQF   wanglaoshi@126.com");
MODULE_DESCRIPTION(" this module description : a testcode for modules");
